﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using MetaCollection.ContentService.Http;
using MetaCollection.Framework.Model;

namespace MetaCollection.ContentService.WebDav
{
	/// <summary>
	/// A WebDav request.
	/// </summary>
	/// <remarks>This class contains properties that map to an HttpRequest and provided WebDav request specific 
	/// information.</remarks>
	public class DavRequest
	{
		/// <summary>
		/// The HttpRequest
		/// </summary>
		private HttpRequest m_Request;

		private DavContext m_DavContext;
		/// <summary>
		/// Initializes a new instance of the <see cref="DavRequest"/> class.
		/// </summary>
		/// <param name="request">The request.</param>
		internal DavRequest(HttpRequest request, DavContext context)
		{
			m_Request = request;
			m_DavContext = context;
		}

		/// <summary>
		/// Gets the Request Body as XML
		/// </summary>
		/// <exception cref="Http.ClientException">A BadRequest client Http Exception is thrown if a request body exists but it
		/// can not be converted into XML</exception>
		/// <value>The XML or null if there is no request body.</value>
		public XElement Xml
		{
			get
			{
				if (m_Request.InputStream.Length != 0)
				{
					try
					{
						return XElement.Load(XmlReader.Create(m_Request.InputStream));
					}
					catch (XmlException)
					{
						throw new Http.ClientException(ClientErrorStatus.BadRequest, "Invalid XML Message Format");
					}
				}
				return null;
			}
		}

		/// <summary>
		/// The HTTP method for the request.
		/// </summary>
		public Http.HttpMethods Method
		{
			get
			{
				string methodName = m_Request.RequestType;
				if (!string.IsNullOrEmpty(m_Request.Headers["IEISPOOP"]))	// The IE team believes they have omnipotent powers over the internet and are able to determine server
					methodName = m_Request.Headers["IEISPOOP"];				// supported request methods types without even bothering to check.  This header in their honor.
				return methodName.ParseHttpMethod();
			}
		}

		/// <summary>
		/// Evaluates the translate header, Returns True if normal request processing should be done on an item, false if raw data should be returned instead
		/// </summary>
		/// <param name="translateHeader">The translate header.</param>
		/// <returns><c>[true]</c> if normal request handling should be used; <c>[false]</c> if the raw untemplated item should be returned instead</returns>
		public bool Translate
		{
			get
			{
				return String.IsNullOrEmpty(m_Request.Headers["Translate"]) ? true : !m_Request.Headers["Translate"].Equals("f", StringComparison.OrdinalIgnoreCase);
			}
		}

		public bool RespondWithJson
		{
			get
			{
				return String.IsNullOrEmpty(m_Request.Headers["RespondWithJson"]) ? false : m_Request.Headers["RespondWithJson"].Equals("t", StringComparison.OrdinalIgnoreCase);
			}
		}

		/// <summary>
		/// Evaluates the depth header and returns the processing depth associated with this request
		/// </summary>
		/// <param name="depthHeader">The depth header.</param>
		/// <returns>The Depth of processing this request applies to</returns>
		public DepthOption Depth()
		{
			string depth = m_Request.Headers["Depth"];
		
			if (!String.IsNullOrEmpty(m_Request.Headers["Depth"]))
			{
				if (m_Request.Headers["Depth"].Equals("infinity", StringComparison.OrdinalIgnoreCase))
					return DepthOption.AllDescendants;
				if (m_Request.Headers["Depth"] == "0")
					return DepthOption.NoDescendants;
				if (m_Request.Headers["Depth"] == "1")
					return DepthOption.ImmediateDescendants;
				throw new Http.ClientException(ClientErrorStatus.BadRequest, String.Format(CultureInfo.CurrentCulture, "Invalid depth header value {0}", depth));
			}
			return DepthOption.Undefined;
		}

		/// <summary>
		/// Determines whether the requested content has been modified since the last request.
		/// If the request has not been modified then the Not Modified (304) headers should be set
		/// on the response.
		/// </summary>
		/// <param name="item">The item instance associated with this request.</param>
		/// <returns>
		/// 	<c>true</c> if the response has changed since the last request; otherwise, <c>false</c>.
		/// </returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.DateTime.TryParse(System.String,System.DateTime@)", Justification="Verified.")]
		public bool IsModifiedSinceLastRequest(IItemInstance item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			DateTime requestDate = DateTime.MinValue;
			DateTime.TryParse(m_Request.Headers["If-Modified-Since"], out requestDate);

			return (item.LastModified.CompareTo(requestDate) > 0);
		}

		/// <summary>
		/// The destination URI for operations like Copy and Move
		/// </summary>
		public Uri Destination
		{
			get
			{
				Uri dest = null;
				if (!String.IsNullOrEmpty(m_Request.Headers["Destination"]))
				{
					try
					{
						dest = new Uri(m_Request.Headers["Destination"].TrimEnd('/'));
						dest = dest.ToCollectionUri();
					}
					catch (UriFormatException)
					{
						throw new Http.ClientException(ClientErrorStatus.BadRequest, "Invalid destination uri");
					}
				}
				return dest;
			}
		}

		/// <summary>
		/// Request option for overwriting files and folders.  Typically assocaited with COPY and MOVE operations 
		/// </summary>
		public bool? Overwrite
		{
			get
			{
				bool? write;
				string header = m_Request.Headers["Overwrite"];
				if (string.IsNullOrEmpty(header))
					write = null;
				else if (header.Equals("f", StringComparison.OrdinalIgnoreCase))
					write = false;
				else if (header.Equals("t", StringComparison.OrdinalIgnoreCase))
					write = true;
				else
					throw new Http.ClientException(ClientErrorStatus.BadRequest, "Invalid overwrite option");
				return write;
			}
		}

		/// <summary>
		/// The requested Template View for this Request
		/// </summary>
		public string Template { get { return m_Request["v"]; } }
		/// <summary>
		/// Gets the ContentType of the incoming Http Request Data
		/// </summary>
		public string ContentType { get { return m_Request.ContentType; } }
		/// <summary>
		/// Gets the Url of the incoming request
		/// </summary>
		public Uri Url { get { return m_Request.Url; } }
		/// <summary>
		/// Gets the contents of the incoming Dav Request body
		/// </summary>
		public Stream InputStream { get { return m_Request.InputStream; } }
		/// <summary>
		/// Form data associated with this request.
		/// </summary>
		public NameValueCollection Form { get { return m_Request.Form; } }
		/// <summary>
		/// A specific version of content requested (null if n/a)
		/// </summary>
		public string SpecificVersion { 
			get 
			{
				return m_Request["ver"];
			} 
		}
		/// <summary>
		/// Gets the specification id from the header(null if non existent)
		/// </summary>
		/// <value>The specification id.</value>
		public Guid? SpecificationId
		{
			get 
			{
				Guid? specId = (Guid?)null;
				string header = m_Request.Headers["X-Specification"];
				if (!string.IsNullOrEmpty(header))
					specId = new Guid(header);
				return specId;
			}
		}
	}
}
